package tcpConn

import (
	"context"
	"fmt"
	"net"
	"server/proto"
	"server/public"
	"sync"
)

/*
*  -*- coding:utf8 -*-
*
*  TCP 通信相关实现
*
*  @Author  : chengms
*  @WebSite : chengms.com
*  @Email   : chengms2012@163.com
*  @TIME    : 2020/8/16 下午6:13
*  @File    : connTcp.go
 */

type TcpServer struct {
	Listener       net.Listener
	ClientConnSet  map[*proto.ClientConn]interface{}
	GroupToMembers map[string][]string
	mu             *sync.RWMutex
}

func NewTcpChatServer() *TcpServer {
	return &TcpServer{
		mu:             &sync.RWMutex{},
		ClientConnSet:  make(map[*proto.ClientConn]interface{}),
		GroupToMembers: make(map[string][]string),
	}
}

func (t *TcpServer) Listen(ctx context.Context, address string) error {
	listener, err := net.Listen("tcp", address)
	if err != nil {
		return err
	}

	t.Listener = listener

	public.Loggerf.Infof("Listening on: %s", address)

	return err
}

func (t *TcpServer) Close(ctx context.Context) error {
	return t.Listener.Close()
}

func (t *TcpServer) Start(ctx context.Context, address string) error {
	if err := t.Listen(ctx, address); err != nil {
		public.Loggerf.Errorf("Server listen addr %s, err: %v", address, err)
		return err
	}

Loop:
	for {
		select {
		case <-ctx.Done():
			public.Loggerf.Info("Chat server is shutting down...")
			//TODO: 停止服务的具体操作
			public.Loggerf.Info("shutdown successfully")
			break Loop
		default:
		}

		conn, err := t.Listener.Accept()
		if err != nil {
			fmt.Println(err)
			continue
		}

		clientConn := t.ClientAccept(conn)
		go t.serve(clientConn)
	}

	return nil
}

func (t *TcpServer) ClientAccept(conn net.Conn) *proto.ClientConn {
	public.Loggerf.Infof("Accepting connection from %s", conn.RemoteAddr().String())

	t.mu.Lock()
	defer t.mu.Unlock()

	cc := &proto.ClientConn{
		Conn:   conn,
		Writer: proto.NewCommandWriter(conn),
	}

	t.ClientConnSet[cc] = struct{}{}
	return cc
}

func (t *TcpServer) Remove(cc *proto.ClientConn) {
	t.mu.Lock()
	defer t.mu.Unlock()

	delete(t.ClientConnSet, cc)
	//TODO: 更新用户状态

}

func (t *TcpServer) serve(cc *proto.ClientConn) {
	cc.Reader = proto.NewCommandReader(cc.Conn)
	defer t.Remove(cc)

	for {
		cmd, err := cc.Reader.Read()
		if err != nil {
			//if err == *net.OpError {
			//    break
			//}
			//if neterr, ok := err.(net.Error); ok && neterr.Timeout() || err == io.EOF {
			//    fmt.Println("Closing connection...")
			//    break // connection will be closed
			//}
			//errStr := err.Error()
			//err1 := err.(*net.OpError)
			//if err == io.EOF || (runtime.GOOS == "windows" &&
			//    strings.Contains(errStr, "An existing connection was forcibly closed by the remote host") ||
			//    strings.Contains(errStr, "An established connection was aborted by the software in your host machine")) ||
			//    strings.Contains(errStr, "connection reset by peer") {
			//    /*
			//       1.io.EOF
			//           正常关闭.指客户端读完服务器发送的数据然后close
			//
			//       2.
			//       connection reset by peer(linux)
			//       An existing connection was forcibly closed by the remote host(windows)
			//           表示客户端 【没有读取/读取部分】就close
			//
			//       3.An established connection was aborted by the software in your host machine(windows)
			//           表示服务器发送数据，客户端已经close,这个经过测试只有在windows上才会出现。linux试了很多遍都是返回io.EOF错误
			//           解决办法就是客户端发送数据的时候需要wait一下，然后再close，这样close的结果就是2了
			//    */
			//} else if err1 != nil && err1.Timeout() {
			//
			//} else {
			//}
			public.Loggerf.Infof("read message err: %T, %v", err, err)
			// client quit
			if err == proto.CloseTcpErr {
				break
			}
			//break
			continue
		}

		TcpClientServe(t, cc, cmd)
	}
}
